

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using SlimDX;
using SlimDX.Direct3D9;

using System.Xml;
using System.Xml.Serialization;

namespace Splino
{
    using Splino.D3D;

    /// <summary>
    /// A Splino render window
    /// </summary>
    public partial class SplinoWindow : Form
    {
        private static Size _largestSplinoWindowSize = new Size();
        /// <summary>
        /// Gets the size of the largest Splino Window
        /// </summary>
        public static Size LargestSplinoWindowSize { get { return _largestSplinoWindowSize; } }

        bool _mouseDown;

        SplinoViewport _splinoViewport;
        /// <summary>
        /// Gets or sets the Splino Viewport contained
        /// in this Splino Panel
        /// </summary>
        public SplinoViewport SplinoViewport
        {
            get { return _splinoViewport; }
            set 
            { 
                _splinoViewport = value;
                value.ViewportWindow = this;
            }
        }

        SplinoFormChild _splinoFormChild;
        /// <summary>
        /// Gets the dummy window this window uses to follow MDI layouts.
        /// </summary>
        public SplinoFormChild SplinoFormChild
        {
            get { return _splinoFormChild; }
        }

        Panels _panelName;
        /// <summary>
        /// Gets or sets the enumerator name of this Splino Panel
        /// </summary>
        public Panels PanelName
        {
            get { return _panelName; }
            set { _panelName = value; }
        }

        Point _cursorWrapOffset = new Point();
        /// <summary>
        /// The difference between mouse positions when the
        /// cursors is wrapped around the Splino Panel
        /// </summary>
        public Point CursorWrapOffset
        {
            get { return _cursorWrapOffset; }
        }

        Point _cursorWrapPrev = new Point();

        Point _cursorStartPosition = new Point();
        /// <summary>
        /// The initial location of the mouse during Viewport
        /// mouse interaction events
        /// </summary>
        public Point CursorStartPosition
        {
            get { return _cursorStartPosition; }
        }

        Point _cursorPreviousPosition = new Point();
        /// <summary>
        /// The last location of the mouse during Viewport
        /// mouse interaction events
        /// </summary>
        public Point CursorPreviousPosition
        {
            get { return _cursorPreviousPosition; }
        }

        int _dx, _dy, _ex, _ey;

        Point _pointTopLeftCorner;
        Point _pointBottomRightCorner;


        /// <summary>The label that displays the type of object snap if one is active</summary>
        public Label SnapLabel { get { return _labelSnap; } }

        bool _docked;
        /// <summary>
        /// Gets or sets whether the Splino Window is docked to the main window.
        /// </summary>
        public bool Docked
        {
            get { return _docked; }
            set { _docked = value; }
        }

        /// <summary>
        /// Gets the control that the Direct3D device will present to
        /// </summary>
        public Panel PresentationPanel { get { return _presentationPanel; } }

        bool _initialized = false;
        /// <summary>
        /// Gets whether the Splino Window components have been initialized.
        /// </summary>
        [XmlIgnore()]
        public bool Initialized 
        { 
            get { return _initialized; }
            set { _initialized = value; }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public SplinoWindow()
        {
            InitializeComponent();
            _splinoFormChild = new SplinoFormChild(this);
            Location = _splinoFormChild.Location;
            this.MouseWheel += new MouseEventHandler(SplinoWindow_MouseWheel);
            this.BackColor = Palette.splino00;
            _labelSnap.BackColor = Palette.splino00;
            _labelSnap.ForeColor = Palette.splino13;
            _presentationPanel.Tag = this;
        }


        /// <summary>
        /// Key Down event handler
        /// </summary>
        /// <param name="e">Key event arguments</param>
        protected override void OnKeyDown(KeyEventArgs e)
        {
            SplinoForm.Instance.SplinoForm_KeyDown(null, e);
            base.OnKeyDown(e);
        }

        /// <summary>
        /// Key Up event handler
        /// </summary>
        /// <param name="e">Key event arguments</param>
        protected override void OnKeyUp(KeyEventArgs e)
        {
            SplinoForm.Instance.SplinoForm_KeyUp(null, e);
            base.OnKeyUp(e);
        }

        private void WrapCursorBegin()
        {
            _pointTopLeftCorner = PointToScreen(PresentationPanel.Location);
            _pointBottomRightCorner = PointToScreen(new Point(ClientSize.Width, ClientSize.Height));
            Cursor.Clip = new Rectangle(_pointTopLeftCorner.X, _pointTopLeftCorner.Y, ClientSize.Width, ClientSize.Height);
        }

        private void WrapCursorAroundPanel3D(MouseEventArgs e)
        {
            _cursorWrapPrev.X = 0;
            _cursorWrapPrev.Y = 0;

            if (e.X == 0)
            {
                Cursor.Position = new Point(_pointBottomRightCorner.X - 2, Cursor.Position.Y);
                _cursorWrapOffset.X -= ClientSize.Width;
                _cursorWrapPrev.X = -ClientSize.Width;
            }
            if (e.X == ClientSize.Width - 1)
            {
                Cursor.Position = new Point(_pointTopLeftCorner.X + 1, Cursor.Position.Y);
                _cursorWrapOffset.X += ClientSize.Width;
                _cursorWrapPrev.X = ClientSize.Width;
            }
            if (e.Y <= 1)
            {
                Cursor.Position = new Point(Cursor.Position.X, _pointBottomRightCorner.Y - 2);
                _cursorWrapOffset.Y -= PresentationPanel.Height;
                _cursorWrapPrev.Y = -PresentationPanel.Height;
            }
            if (e.Y >= ClientSize.Height - 1 - PresentationPanel.Location.Y)
            {
                Cursor.Position = new Point(Cursor.Position.X, _pointTopLeftCorner.Y + 2);
                _cursorWrapOffset.Y += PresentationPanel.Height;
                _cursorWrapPrev.Y = PresentationPanel.Height;
            }
        }

        private void SplinoWindow_MouseDown(object sender, MouseEventArgs e)
        {
            _cursorWrapOffset.X = 0;
            _cursorWrapOffset.Y = 0;
            _cursorStartPosition.X = e.X;
            _cursorStartPosition.Y = e.Y;
            _cursorPreviousPosition.X = e.X;
            _cursorPreviousPosition.Y = e.Y;

            _mouseDown = true;

            CommandManager.Instance.ActiveWindow = this;
        }

        private void SplinoWindow_MouseLeave(object sender, EventArgs e)
        {
            SplinoForm.Instance.WindowMouseOver = null;

            SplinoForm.Instance.ResetCoordStatusText();

            Cursor = Cursors.Default;
        }

        /// <summary>
        /// Mouse move event handler
        /// </summary>
        /// <param name="sender">Event initiator</param>
        /// <param name="e">Mouse event arguments</param>
        public void SplinoWindow_MouseMove(object sender, MouseEventArgs e)
        {
            _dx = e.X - _cursorStartPosition.X + _cursorWrapOffset.X;
            _dy = e.Y - _cursorStartPosition.Y + _cursorWrapOffset.Y;

            _ex = e.X - _cursorPreviousPosition.X + _cursorWrapPrev.X;
            _ey = e.Y - _cursorPreviousPosition.Y + _cursorWrapPrev.Y;

            _cursorPreviousPosition.X = e.X;
            _cursorPreviousPosition.Y = e.Y;
        }

        private void SplinoWindow_MouseUp(object sender, MouseEventArgs e)
        {
            Cursor.Clip = new Rectangle();

            _mouseDown = false;
        }

        private void SplinoWindow_MouseEnter(object sender, EventArgs e)
        {
            Focus();
            SplinoForm.Instance.WindowMouseOver = this;
            CommandManager.Instance.ActiveWindow = this;
        }

        /// <summary>
        /// Mouse wheel event handler
        /// </summary>
        /// <param name="sender">Event initiator</param>
        /// <param name="e">Mouse event arguments</param>
        public void SplinoWindow_MouseWheel(object sender, MouseEventArgs e)
        {
            CommandManager.Instance.ExecuteCommand(
                new Commands.ZoomCameraMouse());
            CommandManager.Instance.ActiveCommandStack.Peek().MouseWheel(this, e);
        }

        /// <summary>
        /// Initializes variables used to track viewport panning with the mouse
        /// </summary>
        public void Command_Pan_MouseDown()
        {
            WrapCursorBegin();
            SplinoViewport.UpdatePanVectors();
            SplinoViewport.BeginPan();
        }

        /// <summary>
        /// Initializes variables used to track viewport zooming with the mouse
        /// </summary>
        public void Command_Zoo_MouseDown()
        {
            WrapCursorBegin();
            _splinoViewport.BeginZoom();
        }

        /// <summary>
        /// Initializes variables used to track viewport rotating with the mouse
        /// </summary>
        public void Command_Rotate_MouseDown()
        {
            WrapCursorBegin();
            _splinoViewport.BeginRotate();
        }

        /// <summary>
        /// Pans the viewport based on the mouse position
        /// </summary>
        /// <param name="e">Mouse event arguments from the Mouse Move event</param>
        public void Command_Pan_MouseMove(MouseEventArgs e)
        {
            WrapCursorAroundPanel3D(e);
            SplinoViewport.Pan(_dx, _dy);
        }

        /// <summary>
        /// Zooms the viewport based on the mouse position
        /// </summary>
        /// <param name="e">Mouse event arguments from the Mouse Move event</param>
        public void Command_Zoo_MouseMove(MouseEventArgs e)
        {
            WrapCursorAroundPanel3D(e);
            SplinoViewport.ZoomCursor(CursorStartPosition.X, CursorStartPosition.Y, _ey);
        }

        /// <summary>
        /// Zooms the Viewport using the mouse wheel
        /// </summary>
        /// <param name="e">Mouse event arguments from the Mouse Move event</param>
        public void Command_ZoomCameraMouse(MouseEventArgs e)
        {
            Point panelScreenLocation = PointToScreen(Location);
            _splinoViewport.ZoomOneDetent(
                PointToClient(Cursor.Position).X,
                PointToClient(Cursor.Position).Y,
                e.Delta);
        }

        /// <summary>
        /// Rotates the viewport based on the mouse position
        /// </summary>
        /// <param name="e">Mouse event arguments from the Mouse Move event</param>
        public void Command_Rotate_MouseMove(MouseEventArgs e)
        {
            WrapCursorAroundPanel3D(e);
            SplinoViewport.Rotate(_ex, _ey);
        }

        /// <summary>
        /// Translates the draw point when the mouse is moved
        /// </summary>
        /// <param name="e">Mouse event arguments from the Mouse Move event</param>
        public void Command_Draw_MouseMove(MouseEventArgs e)
        {
            _splinoViewport.UpdateDrawPoint(e.X, e.Y);
        }

        /// <summary>
        /// Updates the status bar label indicating the world coordinates of the
        /// mouse when its moved over a Viewport
        /// </summary>
        /// <param name="e">Mouse event arguments from the Mouse Move event</param>
        public void Command_Default_MouseMove(MouseEventArgs e)
        {
            Vector3 output = _splinoViewport.ScreenToWorldGrid(e.X, e.Y);
            SplinoForm.Instance.LabelXCoordStatusText = String.Format("x {0:0.000}", output.X);
            SplinoForm.Instance.LabelYCoordStatusText = String.Format("y {0:0.000}", output.Y);
            SplinoForm.Instance.LabelZCoordStatusText = String.Format("z {0:0.000}", output.Z);
        }

        /// <summary>
        /// Creates an empty instance of Mouse event arguments
        /// </summary>
        /// <returns>Empty mouse event arguments</returns>
        public MouseEventArgs GenerateMouseEventArgs()
        {
            return new MouseEventArgs(
                MouseButtons.None,
                0,
                Cursor.Position.X,
                Cursor.Position.Y,
                0);
        }

        /// <summary>
        /// Shows a label on the viewport indicating that an object snap with the snap type
        /// as the label's text and at the specified location.
        /// </summary>
        /// <param name="snapType">The type of object snap</param>
        /// <param name="x">The x location of the label relative to the SplinoWindow</param>
        /// <param name="y">The y location of the label relative to the SplinoWindow</param>
        public void ShowSnapLabel(ObjectSnapType snapType, int x, int y)
        {
            switch (snapType)
            {
                case ObjectSnapType.Center:
                    _labelSnap.Text = "Center";
                    break;
                case ObjectSnapType.CloseSpline:
                    _labelSnap.Text = "Close Spline";
                    break;
                case ObjectSnapType.End:
                    _labelSnap.Text = "Endpoint";
                    break;
                case ObjectSnapType.Intersect:
                    _labelSnap.Text = "Intersection";
                    break;
                case ObjectSnapType.Mid:
                    _labelSnap.Text = "Midpoint";
                    break;
                case ObjectSnapType.Near:
                    _labelSnap.Text = "Near Object";
                    break;
                case ObjectSnapType.Perp:
                    _labelSnap.Text = "Perpendicular";
                    break;
                case ObjectSnapType.Point:
                    _labelSnap.Text = "Point";
                    break;
                case ObjectSnapType.Quadrant:
                    _labelSnap.Text = "Quadrant";
                    break;
                case ObjectSnapType.Tan:
                    _labelSnap.Text = "Tangent";
                    break;
            }
            _labelSnap.Location = new Point(x, y);
            _labelSnap.Visible = true;
        }

        private void SplinoWindow_Paint(object sender, PaintEventArgs e)
        {
            DeviceManager.Instance.RequestRender();
        }

        private void SplinoWindow_Resize(object sender, EventArgs e)
        {
            if (Width > _largestSplinoWindowSize.Width)
                _largestSplinoWindowSize.Width = Width;
            if (Height > _largestSplinoWindowSize.Height)
                _largestSplinoWindowSize.Height = Height;
            //DeviceManager.Instance.Reset();
            if (SplinoViewport != null)
                SplinoViewport.ResizeSwapChain();
        }

        private void SplinoWindow_Move(object sender, EventArgs e)
        {
            if (_mouseDown)
                FormBorderStyle = FormBorderStyle.Sizable;
        }

        private void SplinoWindow_LocationChanged(object sender, EventArgs e)
        {
            if (_docked && 
                _initialized && 
                _splinoFormChild.Location != Location &&
                WindowState != FormWindowState.Maximized)
                Undock();

        }

        /// <summary>
        /// Undocks the window from the main SplinoForm.
        /// </summary>
        public void Undock()
        {
            Point screenLocation = MdiParent.PointToScreen(SplinoForm.Instance.PointToMDIClientArea(Location));
            _docked = false;
            _splinoFormChild.Hide();
            Hide();
            MdiParent = null;
            FormBorderStyle = FormBorderStyle.Sizable;
            Show(SplinoForm.Instance);
            Location = screenLocation;
        }

        /// <summary>
        /// Docks the window the main SplinoForm
        /// </summary>
        public void DockWIndow()
        {
            SplinoWindow splinoWindow = SplinoForm.Instance.CreateSplinoWindow();
            splinoWindow.SplinoViewport = this.SplinoViewport;
            splinoWindow.Text = this.Text;
            splinoWindow.Docked = true;
            Close();
            SplinoForm.Instance.LayoutSplinoWindows();
            DeviceManager.Instance.Viewports.Add(splinoWindow.SplinoViewport);
            //_docked = true;
            //_splinoFormChild.Show();
            ////Hide();
            //MdiParent = SplinoForm.Instance;
            //FormBorderStyle = FormBorderStyle.FixedToolWindow;
            ////Show();
        }

        private void SplinoWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
            SplinoForm.Instance.SplinoWindows.Remove(this);
            DeviceManager.Instance.Viewports.Remove(_splinoViewport);
            _splinoViewport = null;
            _splinoFormChild.Close();
            //_splinoFormChild = null;
        }

        private void SplinoWindow_Load(object sender, EventArgs e)
        {
            Icon = SplinoForm.Instance.Icon;
        }

        private void _presentationPanel_MouseEnter(object sender, EventArgs e)
        {
            //Focus();
            SplinoForm.Instance.WindowMouseOver = this;
            CommandManager.Instance.ActiveWindow = this;
        }

        private void SplinoWindow_Deactivate(object sender, EventArgs e)
        {
            SnapLabel.Visible = false;
        }

        private void dockedToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (_docked)
                Undock();
            else
                DockWIndow();
        }

        private void SplinoWindow_FormClosed(object sender, FormClosedEventArgs e)
        {
            SplinoForm.Instance.LayoutSplinoWindows();
        }

    }

    /// <summary>
    /// Represents the serializable components of a Splino Window for use in XML serialization
    /// </summary>
    public class SplinoWindowXML
    {
        private SplinoViewport _splinoViewport;
        /// <summary>
        /// Gets or sets the Splino Viewport
        /// </summary>
        public SplinoViewport SplinoViewport
        {
            get { return _splinoViewport; }
            set { _splinoViewport = value; }
        }

        private Point _location;
        /// <summary>
        /// Gets or sets the window's location
        /// </summary>
        public Point Location
        {
            get { return _location; }
            set { _location = value; }
        }

        private int _width;
        /// <summary>
        /// Gets or sets the window's width
        /// </summary>
        public int Width
        {
            get { return _width; }
            set { _width = value; }
        }

        private int _height;
        /// <summary>
        /// Gets or sets the window's height
        /// </summary>
        public int Height
        {
            get { return _height; }
            set { _height = value; }
        }

        private bool _docked;
        /// <summary>
        /// Gets or sets whether the window is docked to its MDI parent form
        /// </summary>
        public bool Docked
        {
            get { return _docked; }
            set { _docked = value; }
        }

        private String _text;
        /// <summary>
        /// Gets or sets the window's text
        /// </summary>
        public String Text
        {
            get { return _text; }
            set { _text = value; }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        public SplinoWindowXML() { }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="splinoWindow">The Splino Window being represented in the XML structure</param>
        public SplinoWindowXML(SplinoWindow splinoWindow) 
        {
            _splinoViewport = splinoWindow.SplinoViewport;
            _location = splinoWindow.Location;
            _width = splinoWindow.Width;
            _height = splinoWindow.Height;
            _docked = splinoWindow.Docked;
            _text = splinoWindow.Text;
        }
    }
}